package com.zatech.cgnci.project.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zatech.cgnci.project.base.enums.ClaimAmountTypeEnum;
import com.zatech.cgnci.project.base.enums.ClaimDetailTypeEnum;
import com.zatech.cgnci.project.base.enums.ResponseCodeEnum;
import com.zatech.cgnci.project.base.exception.GlobalException;
import com.zatech.cgnci.project.base.utils.BeanUtils;
import com.zatech.cgnci.project.base.utils.ConvertUtils;
import com.zatech.cgnci.project.base.utils.DateUtils;
import com.zatech.cgnci.project.base.utils.VerifyUtils;
import com.zatech.cgnci.project.dto.CgnciUnearnedPremiumReserveLedgerDTO;
import com.zatech.cgnci.project.dto.ProvisionRegisterExtractDTO;
import com.zatech.cgnci.project.dto.ReportCreateDTO;
import com.zatech.cgnci.project.entity.*;
import com.zatech.cgnci.project.mapper.*;
import com.zatech.cgnci.project.service.CgnciOutstandingClaimsAnnualSummaryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zatech.cgnci.project.service.CgnciReinsuranceRiskInsuredService;
import com.zatech.cgnci.project.service.CgnciReinsuranceRiskLayerService;
import com.zatech.cgnci.project.service.CgnciReinsuranceRiskService;
import com.zatech.cgnci.project.vo.*;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 未决赔款准备金年度汇总表 服务实现类
 * </p>
 *
 * @author Generator
 * @since 2025-02-19
 */
@Service
public class CgnciOutstandingClaimsAnnualSummaryServiceImpl extends ServiceImpl<CgnciOutstandingClaimsAnnualSummaryMapper, CgnciOutstandingClaimsAnnualSummaryDO> implements CgnciOutstandingClaimsAnnualSummaryService {
    @Resource
    private CgnciBusinessProjectMapper projectMapper;
    @Resource
    private CgnciClaimPolicyMapper claimPolicyMapper;
    @Resource
    private CgnciClaimReserveDetailMapper reserveDetailMapper;
    @Resource
    private CgnciReinsuranceRiskInsuredService cgnciReinsuranceRiskInsuredService;
    @Resource
    private CgnciOutstandingClaimsReserveMapper claimsReserveMapper;
    @Resource
    private CgnciReinsuranceRiskService cgnciReinsuranceRiskService;
    @Resource
    private CgnciReinsuranceRiskLayerService cgnciReinsuranceRiskLayerService;
    @Resource
    CgnciClaimSettlementDetailMapper claimSettlementDetailMapper;
    @Resource
    private CgnciReinsuranceInfoMapper infoMapper;
    @Override
    public void annualSummary(ReportCreateDTO extractDTO) {
            //查询所有生效项目
            List<CgnciBusinessProjectDO> projectDOS = projectMapper.selectList(new LambdaQueryWrapper<CgnciBusinessProjectDO>()
                    .eq(CgnciBusinessProjectDO::getStatus, '3')
                    .eq(CgnciBusinessProjectDO::getIsMirror, '0'));
            List<CgnciOutstandingClaimsAnnualSummaryDO> reserveLedgerDOS = new ArrayList<>();
            projectDOS.forEach(projectDO->{
                //查询项目下已创建生效的立案数据
                List<CgnciClaimPolicyVO> projectVO = claimPolicyMapper.getPolicyByAnnual(projectDO.getProjectNo(),extractDTO.getInitialDate(),extractDTO.getPeriodDate());
                if(CollectionUtils.isNotEmpty(projectVO)){
                    List<String> riskSubTypeList = Arrays.asList(projectDO.getRiskSubTypeCode().split(","));
                    riskSubTypeList.forEach(risk->{
                        //查询模版数据
                        CgnciOutstandingClaimsReserveDO claimsReserveDOS = claimsReserveMapper.selectOne(new LambdaQueryWrapper<CgnciOutstandingClaimsReserveDO>()
                                .eq(CgnciOutstandingClaimsReserveDO::getRiskTypeCode,risk));
                        if(Objects.isNull(claimsReserveDOS)){
                            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "险种代码："+risk+"的假设模版数据不存在，请导全数据");
                        }
                        CgnciOutstandingClaimsAnnualSummaryDO reserveLedgerDO = new CgnciOutstandingClaimsAnnualSummaryDO();
                        reserveLedgerDO.setRegisterId(extractDTO.getRegisterId());
                        reserveLedgerDO.setValuationDate(DateUtils.parseDate(extractDTO.getValuationMonth(),DateUtils.FORMAT_8));
                        reserveLedgerDO.setProjectName(projectDO.getProjectName());
                        reserveLedgerDO.setRiskClass(projectDO.getRiskClass());
                        reserveLedgerDO.setRiskSubType(claimsReserveDOS.getRiskType());
                        reserveLedgerDO.setDelayDays(claimsReserveDOS.getDelayDays());
                        reserveLedgerDO.setActualDelayDays(getActualDelayDays(claimsReserveDOS,projectDO,reserveLedgerDO.getValuationDate()));
                        reserveLedgerDO.setPolicyStartDate(projectDO.getStartDate());
                        reserveLedgerDO.setPolicyEndDate(projectDO.getEndDate());
                        reserveLedgerDO.setPolicyAnnual(projectDO.getProjectYear());
                        reserveLedgerDO.setPolicyCurrency(projectDO.getCurrency());
                        BigDecimal engagedCollect = new BigDecimal(0);
                        BigDecimal cededCollect = new BigDecimal(0);
                        BigDecimal inwardAmount = new BigDecimal(0);
                        BigDecimal recoveryAmount = new BigDecimal(0);
                        BigDecimal approvedReserve = new BigDecimal(0);
                        for(CgnciClaimPolicyVO project:projectVO){
                            //查询分入信息下的险类和险种
                            CgnciReinsuranceInfoVO infoVO = getDivideInReinsuranceByInsuranceId(project.getCurrentPolicyId());
                            // 转换为 Map<String, DTO>
                            Map<String, CgnciReinsuranceRiskVO> riskVOMap = infoVO.getRisks().stream()
                                    .collect(Collectors.toMap(CgnciReinsuranceRiskVO::getRiskSubTypeCode, dto -> dto));
                            CgnciReinsuranceRiskVO riskVO = riskVOMap.get(risk);
                            BigDecimal epGross = new BigDecimal(0);
                            if(Objects.nonNull(riskVO)&&CollectionUtils.isNotEmpty(riskVO.getRiskInsureds())){
                               epGross = riskVO.getRiskInsureds().stream()
                                        .map(risk1 -> BeanUtils.sumBigDecimalProperty(risk1.getRiskLayers(), CgnciReinsuranceRiskLayerVO::getGrossPremium))
                                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                            }
                            engagedCollect = engagedCollect.add(epGross);
                            //分出险种信息
                            CgnciReinsuranceInfoVO lfDivideOutReinsuranceByInsuranceId = getLfDivideOutReinsuranceByInsuranceId(project.getCurrentPolicyId(),risk);
                            if(CollectionUtils.isEmpty(lfDivideOutReinsuranceByInsuranceId.getRisks())){
                                cededCollect = cededCollect.add(new BigDecimal(0));
                            }else{
                                List<CgnciReinsuranceRiskInsuredVO> riskLayerList = lfDivideOutReinsuranceByInsuranceId.getRisks().get(0).getRiskInsureds();
                                cededCollect = cededCollect.add(riskLayerList.stream()
                                        .map(riskLayer -> BeanUtils.sumBigDecimalProperty(riskLayer.getRiskLayers(), CgnciReinsuranceRiskLayerVO::getGrossPremium))
                                        .reduce(BigDecimal.ZERO, BigDecimal::add));
                            }
                            List<CgnciClaimSettlementDetailDO> inwardDOS= claimSettlementDetailMapper.selectList(new LambdaQueryWrapper<CgnciClaimSettlementDetailDO>()
                                    .eq(CgnciClaimSettlementDetailDO::getCaseNo,project.getCaseNo())
                                    .eq(CgnciClaimSettlementDetailDO::getDetailType, ClaimDetailTypeEnum.INWARD.getCode())
                                    .eq(CgnciClaimSettlementDetailDO::getAmountType, ClaimAmountTypeEnum.COMPENSATION.getCode())
                                    .eq(CgnciClaimSettlementDetailDO::getRiskType,risk));
                            if(CollectionUtils.isEmpty(inwardDOS)){
                                inwardAmount = inwardAmount.add(new BigDecimal(0));
                            }else{
                                inwardAmount = inwardAmount.add(BeanUtils.sumBigDecimalProperty(inwardDOS,CgnciClaimSettlementDetailDO::getSettlementAmount));
                            }
                            List<CgnciClaimSettlementDetailDO> recoveryDOS= claimSettlementDetailMapper.selectList(new LambdaQueryWrapper<CgnciClaimSettlementDetailDO>()
                                    .eq(CgnciClaimSettlementDetailDO::getCaseNo,project.getCaseNo())
                                    .eq(CgnciClaimSettlementDetailDO::getDetailType, ClaimDetailTypeEnum.RECOVERY.getCode())
                                    .eq(CgnciClaimSettlementDetailDO::getAmountType, ClaimAmountTypeEnum.COMPENSATION.getCode())
                                    .eq(CgnciClaimSettlementDetailDO::getRiskType,risk));
                            if(CollectionUtils.isEmpty(recoveryDOS)){
                                recoveryAmount = recoveryAmount.add(new BigDecimal(0));
                            }else{
                                recoveryAmount = recoveryAmount.add(BeanUtils.sumBigDecimalProperty(recoveryDOS,CgnciClaimSettlementDetailDO::getSettlementAmount));
                            }
                            //赔案准备金信息
                            List<CgnciClaimReserveDetailDO> detailDOS =  reserveDetailMapper.queryDetailByCaseNoAndRiskType(project.getCaseNo(),risk);
                            if(CollectionUtils.isEmpty(detailDOS)){
                                approvedReserve = approvedReserve.add(new BigDecimal(0));
                            }else{
                                approvedReserve = approvedReserve.add((BeanUtils.sumBigDecimalProperty(detailDOS,CgnciClaimReserveDetailDO::getApprovedReserve)));
                            }
                        }
                        BigDecimal epGross = engagedCollect.multiply(new BigDecimal(reserveLedgerDO.getActualDelayDays()));
                        Integer policyStartDays =DateUtils.getDiffDay(reserveLedgerDO.getPolicyStartDate(),reserveLedgerDO.getPolicyEndDate())+1;
                        reserveLedgerDO.setEpGross(epGross.divide(new BigDecimal(policyStartDays), 2, RoundingMode.HALF_UP));//5
                        BigDecimal epNet = cededCollect.multiply(new BigDecimal(reserveLedgerDO.getActualDelayDays()));
                        reserveLedgerDO.setEpNet(epNet.divide(new BigDecimal(policyStartDays), 2, RoundingMode.HALF_UP));//6
                        reserveLedgerDO.setLossRatio(claimsReserveDOS.getLrLossRatio());//7
                        reserveLedgerDO.setUlGross(reserveLedgerDO.getEpGross().multiply(reserveLedgerDO.getLossRatio())); //8
                        reserveLedgerDO.setUlNet(reserveLedgerDO.getEpNet().multiply(reserveLedgerDO.getLossRatio())); //9
                        reserveLedgerDO.setPaidGross(inwardAmount);//10
                        reserveLedgerDO.setPaidNet(recoveryAmount);//11
                        reserveLedgerDO.setCaseGross(approvedReserve);//12
                        reserveLedgerDO.setCaseNet(approvedReserve);//13
                        reserveLedgerDO.setIbnrGross(reserveLedgerDO.getUlGross().subtract(reserveLedgerDO.getPaidGross()).subtract(reserveLedgerDO.getCaseGross()));
                        reserveLedgerDO.setIbnrNet(reserveLedgerDO.getUlNet().subtract(reserveLedgerDO.getPaidNet()).subtract(reserveLedgerDO.getCaseNet()));
                        reserveLedgerDO.setUlaeExpenseRatio(claimsReserveDOS.getClaimsExpenseRatio());//16
                        BigDecimal ulaeExpense = new BigDecimal(0.5).multiply(reserveLedgerDO.getCaseGross()).add(reserveLedgerDO.getIbnrGross());
                        reserveLedgerDO.setUlaeExpense(ulaeExpense.multiply(reserveLedgerDO.getUlaeExpenseRatio()));//17
                        reserveLedgerDO.setRaRatio(claimsReserveDOS.getRiskAdjustedRate());//18
                        BigDecimal raGross = reserveLedgerDO.getCaseGross().add(reserveLedgerDO.getIbnrGross()).add(reserveLedgerDO.getUlaeExpense());
                        reserveLedgerDO.setRaGross(raGross.multiply(reserveLedgerDO.getRaRatio()));//19
                        BigDecimal raNet = reserveLedgerDO.getCaseNet().add(reserveLedgerDO.getIbnrNet()).add(reserveLedgerDO.getUlaeExpense());
                        reserveLedgerDO.setRaNet(raNet.multiply(reserveLedgerDO.getRaRatio()));
                        reserveLedgerDO.setGross(raGross.add(reserveLedgerDO.getRaGross()));
                        reserveLedgerDO.setNet(raNet.add(reserveLedgerDO.getRaNet()));
                        reserveLedgerDO.setCreateTime(new Date());
                        reserveLedgerDOS.add(reserveLedgerDO);
                    });
                }
            });
            if (VerifyUtils.isEmpty(reserveLedgerDOS)) {
                return;
            }
            //汇总计算
            summary(reserveLedgerDOS);
            //批量分批插入数据
            batchInsertOutstanding(reserveLedgerDOS);
        }

    /**
     * 年度汇总计算
     * @param reserveLedgerDOS
     */
    private void summary(List<CgnciOutstandingClaimsAnnualSummaryDO> reserveLedgerDOS) {
        CgnciOutstandingClaimsAnnualSummaryDO summaryDO = new CgnciOutstandingClaimsAnnualSummaryDO();
        summaryDO.setRegisterId(reserveLedgerDOS.get(0).getRegisterId());
        summaryDO.setProjectName("年度汇总");
        summaryDO.setEpGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getEpGross));
        summaryDO.setEpNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getEpNet));
        summaryDO.setUlGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getUlGross));
        summaryDO.setUlNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getUlNet));
        summaryDO.setPaidGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getPaidGross));
        summaryDO.setPaidNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getPaidNet));
        summaryDO.setCaseGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getCaseGross));
        summaryDO.setCaseNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getCaseNet));
        summaryDO.setIbnrGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getIbnrGross));
        summaryDO.setIbnrNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getIbnrNet));
        summaryDO.setUlaeExpense(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getUlaeExpense));
        summaryDO.setRaGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getRaGross));
        summaryDO.setRaNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getRaNet));
        summaryDO.setGross(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getGross));
        summaryDO.setNet(BeanUtils.sumBigDecimalProperty(reserveLedgerDOS,CgnciOutstandingClaimsAnnualSummaryDO::getNet));
        summaryDO.setCreateTime(DateUtils.getAfterDateByDay(new Date(),1));//用作导出排序用，汇总数据要放最后面
        reserveLedgerDOS.add(summaryDO);
    }

    /**
     * 未决赔款准备金保存
     * @param reserveLedgerDOS
     */
    public void batchInsertOutstanding(List<CgnciOutstandingClaimsAnnualSummaryDO> reserveLedgerDOS) {
        int batchSize = 500;
        int totalSize = reserveLedgerDOS.size();
        int batchCount = (totalSize + batchSize - 1) / batchSize;

        for (int i = 0; i < batchCount; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min(fromIndex + batchSize, totalSize);
            List<CgnciOutstandingClaimsAnnualSummaryDO> batchList = reserveLedgerDOS.subList(fromIndex, toIndex);
            try {
                this.saveBatch(batchList);
            } catch (Exception e) {
                throw e; // 抛出异常，回滚事务
            }
        }
    }
    /**
     * 计算实际报案延迟天数
     * @param claimsReserveDOS
     * @param projectDO
     * @return
     */
    private Double getActualDelayDays(CgnciOutstandingClaimsReserveDO claimsReserveDOS, CgnciBusinessProjectDO projectDO,Date valuationDate) {
        Integer days = DateUtils.getDiffDay(projectDO.getStartDate(),valuationDate);
        Date newDate = DateUtils.getAfterDateByDay(projectDO.getEndDate(),claimsReserveDOS.getDelayDays().intValue());
        if(days<claimsReserveDOS.getDelayDays()){
            return days.doubleValue();
        }
        if(DateUtils.isEntryDateBeforeStartDate(valuationDate,newDate)){
            return DateUtils.getDiffDay(valuationDate,newDate).doubleValue();
        }
        if(days>=claimsReserveDOS.getDelayDays() && DateUtils.lessThanEqualTo(newDate,valuationDate)){
            return claimsReserveDOS.getDelayDays();
        }
        return 0.0;
    }

    /**
     * 查询分入信息下的险类和险种
     * @param insuranceId
     * @return
     */
    private CgnciReinsuranceInfoVO getDivideInReinsuranceByInsuranceId(Long insuranceId) {
        CgnciReinsuranceInfoDO divideInDO = infoMapper.selectOne(new LambdaQueryWrapper<CgnciReinsuranceInfoDO>()
                .eq(CgnciReinsuranceInfoDO::getBusinessId, insuranceId).eq(CgnciReinsuranceInfoDO::getReinsuranceType, 1));
        if (VerifyUtils.isNotEmpty(divideInDO)) {
            CgnciReinsuranceInfoVO divideInVO = ConvertUtils.convert(divideInDO, CgnciReinsuranceInfoVO.class);
            // 分入险种
            List<CgnciReinsuranceRiskDO> divideInRiskDOList = cgnciReinsuranceRiskService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskDO>()
                    .eq(CgnciReinsuranceRiskDO::getReinsuranceId, divideInDO.getReinsuranceId()));
            if (VerifyUtils.isNotEmpty(divideInRiskDOList)) {
                List<CgnciReinsuranceRiskVO> divideInRiskVOList = ConvertUtils.convert(divideInRiskDOList, CgnciReinsuranceRiskVO.class);
                List<Long> reinsuranceRiskIds = divideInRiskVOList.stream().map(CgnciReinsuranceRiskVO::getReinsuranceRiskId).collect(Collectors.toList());

                Map<Long, List<CgnciReinsuranceRiskInsuredVO>> reinsuranceRiskIdMap = new HashMap<>();
                Map<Long, List<CgnciReinsuranceRiskLayerVO>> reinsuranceRiskInsuredIdMap = new HashMap<>();

                // 分入险种标的
                List<CgnciReinsuranceRiskInsuredDO> divideInRiskInsuredDOList = cgnciReinsuranceRiskInsuredService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskInsuredDO>()
                        .in(CgnciReinsuranceRiskInsuredDO::getReinsuranceRiskId, reinsuranceRiskIds));
                if (VerifyUtils.isNotEmpty(divideInRiskInsuredDOList)) {
                    List<CgnciReinsuranceRiskInsuredVO> divideInRiskInsuredVOList = ConvertUtils.convert(divideInRiskInsuredDOList, CgnciReinsuranceRiskInsuredVO.class);
                    // 分入险种标的ID集合
                    List<Long> riskInsuredIds = divideInRiskInsuredVOList.stream().map(CgnciReinsuranceRiskInsuredVO::getRiskInsuredId).collect(Collectors.toList());
                    // 分入险种标的根据分入险种id分组
                    reinsuranceRiskIdMap = divideInRiskInsuredVOList.stream().collect(Collectors.groupingBy(CgnciReinsuranceRiskInsuredVO::getReinsuranceRiskId));

                    // 分入险种层
                    List<CgnciReinsuranceRiskLayerDO> divideInRiskLayerDOList = cgnciReinsuranceRiskLayerService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskLayerDO>()
                            .in(CgnciReinsuranceRiskLayerDO::getReinsuranceRiskId, riskInsuredIds));
                    if (VerifyUtils.isNotEmpty(divideInRiskLayerDOList)) {
                        List<CgnciReinsuranceRiskLayerVO> divideInRiskLayerVOList = ConvertUtils.convert(divideInRiskLayerDOList, CgnciReinsuranceRiskLayerVO.class);
                        // 分入险种层根据分入险种标的分组
                        reinsuranceRiskInsuredIdMap = divideInRiskLayerVOList.stream().collect(Collectors.groupingBy(CgnciReinsuranceRiskLayerVO::getReinsuranceRiskId));
                    }
                }

                // 组装VO
                Map<Long, List<CgnciReinsuranceRiskInsuredVO>> finalRiskIdMap = reinsuranceRiskIdMap;
                Map<Long, List<CgnciReinsuranceRiskLayerVO>> finalRiskInsuredIdMap = reinsuranceRiskInsuredIdMap;
                divideInRiskVOList.forEach(risk -> {
                    List<CgnciReinsuranceRiskInsuredVO> riskInsuredVOS = finalRiskIdMap.get(risk.getReinsuranceRiskId());
                    riskInsuredVOS.forEach(riskInsured -> {
                        List<CgnciReinsuranceRiskLayerVO> riskLayerVOS = finalRiskInsuredIdMap.get(riskInsured.getRiskInsuredId());
                        riskInsured.setRiskLayers(riskLayerVOS);
                    });
                    risk.setRiskInsureds(riskInsuredVOS);
                });
                divideInVO.setRisks(divideInRiskVOList);
            }
            return divideInVO;
        }
        return null;
    }

    /**
     * 查询分出险种信息
     * @param insuranceId
     * @param riskClassCode
     * @return
     */
    private CgnciReinsuranceInfoVO getLfDivideOutReinsuranceByInsuranceId(Long insuranceId,String riskClassCode) {
        CgnciReinsuranceInfoDO lfDivideOutDO = infoMapper.selectOne(new LambdaQueryWrapper<CgnciReinsuranceInfoDO>()
                .eq(CgnciReinsuranceInfoDO::getBusinessId, insuranceId).eq(CgnciReinsuranceInfoDO::getReinsuranceType, 3));
        if (VerifyUtils.isNotEmpty(lfDivideOutDO)) {
            CgnciReinsuranceInfoVO lfDivideOutVO = ConvertUtils.convert(lfDivideOutDO, CgnciReinsuranceInfoVO.class);
            List<CgnciReinsuranceRiskDO> lfDivideOutRiskDOList = cgnciReinsuranceRiskService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskDO>()
                    .eq(CgnciReinsuranceRiskDO::getReinsuranceId, lfDivideOutDO.getReinsuranceId())
                    .eq(CgnciReinsuranceRiskDO::getRiskClassCode, riskClassCode));
            if (VerifyUtils.isNotEmpty(lfDivideOutRiskDOList)) {
                List<CgnciReinsuranceRiskVO> lfDivideOutRiskRiskVOList = ConvertUtils.convert(lfDivideOutRiskDOList, CgnciReinsuranceRiskVO.class);
                List<Long> reinsuranceRiskIds = lfDivideOutRiskRiskVOList.stream().map(CgnciReinsuranceRiskVO::getReinsuranceRiskId).collect(Collectors.toList());

                Map<Long, List<CgnciReinsuranceRiskInsuredVO>> reinsuranceRiskIdMap = new HashMap<>();
                Map<Long, List<CgnciReinsuranceRiskLayerVO>> reinsuranceRiskInsuredIdMap = new HashMap<>();

                // 分出险种标的
                List<CgnciReinsuranceRiskInsuredDO> lfDivideOutRiskInsuredDOList = cgnciReinsuranceRiskInsuredService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskInsuredDO>()
                        .in(CgnciReinsuranceRiskInsuredDO::getReinsuranceRiskId, reinsuranceRiskIds));
                if (VerifyUtils.isNotEmpty(lfDivideOutRiskInsuredDOList)) {
                    List<CgnciReinsuranceRiskInsuredVO> lfDivideOutRiskInsuredVOList = ConvertUtils.convert(lfDivideOutRiskInsuredDOList, CgnciReinsuranceRiskInsuredVO.class);
                    // 临分分出险种标的ID集合
                    List<Long> riskInsuredIds = lfDivideOutRiskInsuredVOList.stream().map(CgnciReinsuranceRiskInsuredVO::getRiskInsuredId).collect(Collectors.toList());
                    // 分入险种标的根据分入险种id分组
                    reinsuranceRiskIdMap = lfDivideOutRiskInsuredVOList.stream().collect(Collectors.groupingBy(CgnciReinsuranceRiskInsuredVO::getReinsuranceRiskId));

                    // 临分分出险种层
                    List<CgnciReinsuranceRiskLayerDO> lfDivideOutRiskLayerDOList = cgnciReinsuranceRiskLayerService.list(new LambdaQueryWrapper<CgnciReinsuranceRiskLayerDO>()
                            .in(CgnciReinsuranceRiskLayerDO::getReinsuranceRiskId, riskInsuredIds));
                    if (VerifyUtils.isNotEmpty(lfDivideOutRiskLayerDOList)) {
                        List<CgnciReinsuranceRiskLayerVO> lfDivideOutRiskLayerVOList = ConvertUtils.convert(lfDivideOutRiskLayerDOList, CgnciReinsuranceRiskLayerVO.class);
                        // 分入险种层根据分入险种标的分组
                        reinsuranceRiskInsuredIdMap = lfDivideOutRiskLayerVOList.stream().collect(Collectors.groupingBy(CgnciReinsuranceRiskLayerVO::getReinsuranceRiskId));
                    }
                }

                // 组装VO
                Map<Long, List<CgnciReinsuranceRiskInsuredVO>> finalReinsuranceRiskIdMap = reinsuranceRiskIdMap;
                Map<Long, List<CgnciReinsuranceRiskLayerVO>> finalReinsuranceRiskInsuredIdMap = reinsuranceRiskInsuredIdMap;
                lfDivideOutRiskRiskVOList.forEach(risk -> {
                    List<CgnciReinsuranceRiskInsuredVO> riskInsuredVOS = finalReinsuranceRiskIdMap.get(risk.getReinsuranceRiskId());
                    riskInsuredVOS.forEach(riskInsured -> {
                        List<CgnciReinsuranceRiskLayerVO> riskLayerVOS = finalReinsuranceRiskInsuredIdMap.get(riskInsured.getRiskInsuredId());
                        riskInsured.setRiskLayers(riskLayerVOS);
                    });
                    risk.setRiskInsureds(riskInsuredVOS);
                });
                lfDivideOutVO.setRisks(lfDivideOutRiskRiskVOList);
            }
            return lfDivideOutVO;
        }
        return null;
    }

    /**
     * 提取未决赔款准备金台账文件
     *
     * @param registerId
     * @return ExcelWriter
     */
    @Override
    public ExcelWriter outstandingClaimsAnnualSummary(Long registerId) {
        List<CgnciOutstandingClaimsAnnualSummaryDO> reserveLedgerDOS = this.baseMapper.selectList(new LambdaQueryWrapper<CgnciOutstandingClaimsAnnualSummaryDO>()
                    .eq(CgnciOutstandingClaimsAnnualSummaryDO::getRegisterId, registerId)
                     .orderByAsc(CgnciOutstandingClaimsAnnualSummaryDO::getCreateTime));
        ExcelWriter writer = ExcelUtil.getWriter(true);
        try {
            List<String> title = CollUtil.newArrayList(
                    "评估日期",
                    "项目名称",
                    "险种大类",
                    "险种",
                    "报案延迟天数",
                    "实际报案延迟天数",
                    "起保日期",
                    "终保日期",
                    "保单年",
                    "币种",
                    "EP（Gross）-已赚保费",
                    "EP（Net）-已赚保费",
                    "LR%-损失率",
                    "UL（Gross）-最终赔付额",
                    "UL（Net）-最终赔付额",
                    "Paid（Gross）-已付赔款",
                    "Paid（Net）-已付赔款",
                    "Case（Gross）-个案准备金",
                    "Case（Net）-个案准备金",
                    "IBNR（Gross）-已发生未报案赔款准备金",
                    "IBNR（Net）-已发生未报案赔款准备金",
                    "ULAE%-间接理赔费用率",
                    "ULAE-间接理赔费用",
                    "RA%-风险调整费率",
                    "RA（Gross）-风险调整",
                    "RA（Net）-风险调整",
                    "未决赔款准备金（Gross）（原币）",
                    "未决赔款准备金（Net）（原币）");
            writer.writeHeadRow(title);
            for (CgnciOutstandingClaimsAnnualSummaryDO reserveLedgerDO : reserveLedgerDOS) {
                writer.writeRow(Arrays.asList(
                        DateUtils.formatDate(reserveLedgerDO.getValuationDate(), DateUtils.FORMAT_3),
                        reserveLedgerDO.getProjectName(),
                        reserveLedgerDO.getRiskClass(),
                        reserveLedgerDO.getRiskSubType(),
                        reserveLedgerDO.getDelayDays(),
                        reserveLedgerDO.getActualDelayDays(),
                        DateUtils.formatDate(reserveLedgerDO.getPolicyStartDate(), DateUtils.FORMAT_3),
                        DateUtils.formatDate(reserveLedgerDO.getPolicyEndDate(), DateUtils.FORMAT_3),
                        reserveLedgerDO.getPolicyAnnual(),
                        reserveLedgerDO.getPolicyCurrency(),
                        reserveLedgerDO.getEpGross(),
                        reserveLedgerDO.getEpNet(),
                        reserveLedgerDO.getLossRatio()+"%",
                        reserveLedgerDO.getUlGross(),
                        reserveLedgerDO.getUlNet(),
                        reserveLedgerDO.getPaidGross(),
                        reserveLedgerDO.getPaidNet(),
                        reserveLedgerDO.getCaseGross(),
                        reserveLedgerDO.getCaseNet(),
                        reserveLedgerDO.getIbnrGross(),
                        reserveLedgerDO.getIbnrNet(),
                        reserveLedgerDO.getUlaeExpenseRatio()+"%",
                        reserveLedgerDO.getUlaeExpense(),
                        reserveLedgerDO.getRaRatio()+"%",
                        reserveLedgerDO.getRaGross(),
                        reserveLedgerDO.getRaNet(),
                        reserveLedgerDO.getGross(),
                        reserveLedgerDO.getNet()
                ));
            }
        } catch (Exception e) {
            log.error("导出汇总失败", e);
            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "导出汇总失败");
        }
        return writer;
    }
}
